/*
 * Copyright (c) 2023-2024 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.module.dict.api.repository.impl;

import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
import com.baomidou.dynamic.datasource.creator.druid.DruidConfig;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Arrays;
import javax.sql.DataSource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.elsfs.cloud.common.core.utils.SpringContextHolder;
import org.elsfs.cloud.common.mybatis.repository.ElsfsCrudRepositoryImpl;
import org.elsfs.cloud.common.util.lang.StringUtils;
import org.elsfs.cloud.module.dict.api.entity.GenDatasourceConf;
import org.elsfs.cloud.module.dict.api.enums.DsConfTypeEnum;
import org.elsfs.cloud.module.dict.api.enums.DsJdbcUrlEnum;
import org.elsfs.cloud.module.dict.api.repository.GenDatasourceConfRepository;
import org.elsfs.cloud.module.dict.api.repository.mapper.GenDatasourceConfMapper;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.stereotype.Service;

/**
 * 数据源表
 *
 * @author zeng
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GenDatasourceConfRepositoryImpl
    extends ElsfsCrudRepositoryImpl<GenDatasourceConfMapper, GenDatasourceConf, String>
    implements GenDatasourceConfRepository {

  @Override
  public boolean getAllowEditState() {
    return true;
  }

  private final StringEncryptor stringEncryptor;

  private final DefaultDataSourceCreator druidDataSourceCreator;

  /**
   * 保存数据源并且加密
   *
   * @param conf 数据源信息
   * @return Boolean
   */
  @Override
  public Boolean saveDsByEnc(GenDatasourceConf conf) {
    // 校验配置合法性
    if (!checkDataSource(conf)) {
      return Boolean.FALSE;
    }

    // 添加动态数据源
    addDynamicDataSource(conf);

    // 更新数据库配置
    conf.setPassword(stringEncryptor.encrypt(conf.getPassword()));
    this.baseMapper.insert(conf);
    return Boolean.TRUE;
  }

  /**
   * 更新数据源
   *
   * @param conf 数据源信息
   * @return Boolean
   */
  @Override
  public Boolean updateDsByEnc(GenDatasourceConf conf) {
    if (!checkDataSource(conf)) {
      return Boolean.FALSE;
    }
    // 先移除
    DynamicRoutingDataSource dynamicRoutingDataSource =
        SpringContextHolder.getBean(DynamicRoutingDataSource.class);
    dynamicRoutingDataSource.removeDataSource(baseMapper.selectById(conf.getId()).getName());

    // 再添加
    addDynamicDataSource(conf);

    // 更新数据库配置
    if (StringUtils.isNotBlank(conf.getPassword())) {
      conf.setPassword(stringEncryptor.encrypt(conf.getPassword()));
    }
    this.baseMapper.updateById(conf);
    return Boolean.TRUE;
  }

  /**
   * 通过数据源名称删除
   *
   * @param dsIds 数据源ID
   * @return Boolean
   */
  @Override
  public Boolean removeByDsId(Long[] dsIds) {
    DynamicRoutingDataSource dynamicRoutingDataSource =
        SpringContextHolder.getBean(DynamicRoutingDataSource.class);
    this.baseMapper
        .selectByIds(Arrays.stream(dsIds).toList())
        .forEach(ds -> dynamicRoutingDataSource.removeDataSource(ds.getName()));
    this.baseMapper.selectByIds(Arrays.stream(dsIds).toList());
    return Boolean.TRUE;
  }

  /**
   * 添加动态数据源
   *
   * @param conf 数据源信息
   */
  @Override
  public void addDynamicDataSource(GenDatasourceConf conf) {
    DataSourceProperty dataSourceProperty = new DataSourceProperty();
    dataSourceProperty.setPoolName(conf.getName());
    dataSourceProperty.setUrl(conf.getUrl());
    dataSourceProperty.setUsername(conf.getUsername());
    dataSourceProperty.setPassword(conf.getPassword());

    // 增加 ValidationQuery 参数
    DruidConfig druidConfig = new DruidConfig();
    dataSourceProperty.setDruid(druidConfig);
    DataSource dataSource = druidDataSourceCreator.createDataSource(dataSourceProperty);

    DynamicRoutingDataSource dynamicRoutingDataSource =
        SpringContextHolder.getBean(DynamicRoutingDataSource.class);
    dynamicRoutingDataSource.addDataSource(dataSourceProperty.getPoolName(), dataSource);
  }

  /**
   * 校验数据源配置是否有效
   *
   * @param conf 数据源信息
   * @return 有效/无效
   */
  @Override
  public Boolean checkDataSource(GenDatasourceConf conf) {
    String url;
    // JDBC 配置形式
    if (DsConfTypeEnum.JDBC.getType().equals(conf.getConfType())) {
      url = conf.getUrl();
    } else if (DsJdbcUrlEnum.MSSQL.getDbName().equals(conf.getDsType())) {
      // 主机形式 sql server 特殊处理
      DsJdbcUrlEnum urlEnum = DsJdbcUrlEnum.get(conf.getDsType());
      url = String.format(urlEnum.getUrl(), conf.getHost(), conf.getPort(), conf.getDsName());
    } else {
      DsJdbcUrlEnum urlEnum = DsJdbcUrlEnum.get(conf.getDsType());
      url = String.format(urlEnum.getUrl(), conf.getHost(), conf.getPort(), conf.getDsName());
    }

    conf.setUrl(url);

    try (Connection connection =
        DriverManager.getConnection(url, conf.getUsername(), conf.getPassword())) {
      connection.prepareStatement("select 1");
    } catch (SQLException e) {
      LOGGER.error("数据源配置 {} , 获取链接失败", conf.getName(), e);
      throw new RuntimeException("数据库配置错误，链接失败");
    }
    return Boolean.TRUE;
  }
}
